home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-10-17 | 12.4 KB | 432 lines | [TEXT/CWIE] |
- // Game.c -- Contains logic to control high-level game activity
-
- // Necessary includes (uncomment or precompile)
- #include "Sound.h"
- #include "QDOffscreen.h"
- #include "Game.h"
- #include "CTB.h"
-
- // Useful literals
- // Metrics
- #define kPieceWidth 43 // Width and height in pixels of a square
- #define kOffset 42 // From edge of one to edge of adjacent
- #define kTitleBarHeight 18 // Window's title bar height?
- #define kNumCols 8 // # of Vertical columns (max = 2^3 = 8)
- #define kNumRows 8 // # of Horizontal rows (max = 2^3 = 8)
- #define kMargin 20 // Window distance from edges of gray rgn
-
- // Square/face status
- #define kEmpty 0 // Identifies an empty square
- #define kHealthy 1 // Square with healthy face
- #define kStruckLocally 2 // Square with face struck by local player
- #define kStruckByOpponent 3 // Square with face struck by opponent
-
- // Constraints
- #define kDyingTime 60 // Number of ticks it takes a face to die
- #define kMaxMade 5 // # faces allowed made locally at a time
- #define kWinningScore 25 // Number of faces struck to win the game
-
- // Game Actions
- #define kActionMadeFace 1
- #define kActionStruckFace 2
- #define kActionNewGame 3
-
- // Resource IDs
- #define rFirstGraphic 128 // Resource ID of first graphic PICT
- #define rStrikeSound 128 // Resource ID of striking SND
- #define rGenAlert 129 // Resource ID of ALRT that shows scores
-
- typedef struct {
- short occupant; // kEmpty, kHealthy, kStruckLocally, kStruckByOpponent
- long age; // TickCount() at status change
- Rect boundsRect; // Bounding rectangle for gSquare
- Boolean madeLocally;// Keep track of whether made locally
- } contents;
-
- static WindowPtr gGameWindow; // Pointer to our game's window
- static PixMapHandle gPixMapHand[4]; // PixMaps describing graphics
- static contents gSquare[kNumCols][kNumRows]; // Array of squares
- static short gMadeCount, // #locally-made faces
- gLocalStruckCount,// #locally-struck faces
- gOpponentStruckCount;// #opponent-struck
- static Handle gStrikeHand; // Handle to Strike SND
- static SndChannelPtr gSndChannelPtr;// Ptr to Channel to play SND
- static Rect gPicRect; // "Normalized" PICT Rect
-
- //**********************************************************
- //
- // PlayStrikeSound - Plays sound of striking a face
- //
- //**********************************************************
- static void PlayStrikeSound(void)
- {
- if (gStrikeHand && gSndChannelPtr)
- SndPlay(gSndChannelPtr, (SndListHandle)gStrikeHand, true);
- }
-
- //**********************************************************
- //
- // ShowScores - Poses alert which shows scores at game end
- //
- //**********************************************************
- void ShowScores(void)
- {
- Str255 sYourScore, sOpponentScore;
- long lYourScore, lOpponentScore;
-
- if (gLocalStruckCount+gOpponentStruckCount < 1)
- return; // don't bother if no faces struck
-
- lYourScore = gLocalStruckCount;
- NumToString(lYourScore, sYourScore);
- lOpponentScore = gOpponentStruckCount;
- NumToString(lOpponentScore, sOpponentScore);
- if (lYourScore > lOpponentScore)
- ParamText("\pYou win! Your Score: ", sYourScore,
- "\p, Opponent's Score: ", sOpponentScore);
- else if (lYourScore < lOpponentScore)
- ParamText("\pYou lose… Your Score: ", sYourScore,
- "\p, Opponent's Score: ", sOpponentScore);
- else
- ParamText("\pIt's a tie… Your Score: ", sYourScore,
- "\p, Opponent's Score: ", sOpponentScore);
- Alert(rGenAlert, nil);
- }
-
- //**********************************************************
- //
- // DrawSquare - Copies square's graphics to Rect for h, v
- //
- //**********************************************************
- static void DrawSquare(short h, short v)
- {
- GrafPtr oldPort;
- Rect destRect;
-
- if ((h >= 0) && (h < kNumCols) && (v >= 0) && (v < kNumRows)) {
- GetPort(&oldPort);
- SetPort(gGameWindow);
- CopyBits((BitMap*)(*gPixMapHand[gSquare[h][v].occupant]),
- &(gGameWindow->portBits), &gPicRect,
- &(gSquare[h][v].boundsRect), srcCopy, nil);
- SetPort(oldPort);
- }
- }
-
- //**********************************************************
- //
- // NewGame - Sets up window and counters for new game, redraws
- //
- //**********************************************************
- void NewGame(void)
- {
- short h, v;
- OSErr theErr;
-
- gMadeCount = 0;
- gLocalStruckCount = 0;
- gOpponentStruckCount = 0;
-
- for (h = 0; h < kNumCols; h++)
- for (v = 0; v < kNumRows; v++) {
- gSquare[h][v].occupant = kEmpty;
- DrawSquare(h,v);
- }
- }
-
- //**********************************************************
- //
- // MakeGWorld - Returns Ptr to a New GWorld used for graphics
- //
- //**********************************************************
- static GWorldPtr MakeGWorld(Rect *boundsPtr)
- // borrowed GWorlds from MacTech 10, 5: Dave Mark
- {
- GWorldPtr newGWorld;
-
- if (NewGWorld(&newGWorld, 0, boundsPtr, nil, nil, noNewDevice))
- return nil; // error occurred
- else
- return newGWorld;
- }
-
- //**********************************************************
- //
- // NewGameWindow - Creates game's window and supporting stuff
- //
- //**********************************************************
- WindowPtr NewGameWindow(void)
- {
- Rect windRect, r;
- short h, v, i;
- unsigned long rs;
- PicHandle picHand;
- GWorldPtr picWorldPtr;
-
- gGameWindow = nil;
- GetDateTime(&rs); // create "unique random seed"
- qd.randSeed = rs;
- gStrikeHand = nil; // load strike sound and channel
- gSndChannelPtr = nil;
- if (!SndNewChannel(&gSndChannelPtr, 0, initStereo, nil)) {
- gStrikeHand = GetResource('snd ', rStrikeSound);
- if (gStrikeHand) {
- MoveHHi(gStrikeHand);
- HLock(gStrikeHand);
- }
- }
- SetRect(&windRect, kMargin, // create game window
- kMargin+kTitleBarHeight+LMGetMBarHeight(),
- kMargin+kNumCols*kOffset+1,
- kMargin+kTitleBarHeight+LMGetMBarHeight()+kNumRows*kOffset+1);
- gGameWindow = NewCWindow(nil, &windRect, "\pHave a nice day!",
- false, rDocProc+2, (WindowPtr)-1, true, 0);
- for (h = 0; h < kNumCols; h++)
- for (v = 0; v < kNumRows; v++) { // set up "empty" game window
- gSquare[h][v].occupant = kEmpty;
- SetRect(&(gSquare[h][v].boundsRect), h*kOffset, v*kOffset,
- h*kOffset + kPieceWidth, v*kOffset +kPieceWidth);
- }
- SetRect(&gPicRect, 0, 0, kPieceWidth, kPieceWidth);
- for (i = 0; i <= 3; i++) // Load graphics
- if (picHand = GetPicture(i+rFirstGraphic)) {
- picWorldPtr = MakeGWorld(&gPicRect);
- gPixMapHand[i] = GetGWorldPixMap(picWorldPtr);
- LockPixels(gPixMapHand[i]);
- SetGWorld(picWorldPtr, nil);
- HLock((Handle)picHand);
- DrawPicture(picHand, &gPicRect);
- HUnlock((Handle)picHand);
- ReleaseResource((Handle)picHand);
- picHand = nil;
- } // Could abort if picture handle is not allocated
- ShowWindow(gGameWindow);
-
- return gGameWindow;
- }
-
- //**********************************************************
- //
- // KillStruckFaces - Hunts down struck faces and kills them
- //
- //**********************************************************
- static void KillStruckFaces(void)
- {
- short h, v;
-
- for (h = 0; h < kNumCols; h++)
- for (v = 0; v < kNumRows; v++)
- if (gSquare[h][v].occupant > 1)
- if (TickCount() > (gSquare[h][v].age + kDyingTime)) {
- gSquare[h][v].occupant = kEmpty;
- gSquare[h][v].age = TickCount();
- DrawSquare(h,v);
- if (gSquare[h][v].madeLocally)
- --gMadeCount;
- }
- }
-
- //*****************************************************************
- //
- // UpdateGameWindow - Draws any squares necessary for update event
- //
- //*****************************************************************
- void UpdateGameWindow(void)
- {
- GrafPtr oldPort;
- Rect UDRect;
- RgnHandle UDRgn;
- short h, v, v1, v2, h1, h2;
-
- GetPort(&oldPort);
- SetPort(gGameWindow);
- UDRgn = ((WindowPeek)gGameWindow)->updateRgn;
- UDRect = (*UDRgn)->rgnBBox;
- GlobalToLocal(&topLeft(UDRect)); // normalize
- GlobalToLocal(&botRight(UDRect));
-
- v1 = UDRect.top/kOffset; // convert to range of squares
- v2 = UDRect.bottom/kOffset;
- h1 = UDRect.left/kOffset;
- h2 = UDRect.right/kOffset;
-
- BeginUpdate(gGameWindow); // draw the squares in range
- for (v = v1; v <= v2; v++)
- for (h = h1; h <= h2; h++)
- DrawSquare(h, v);
- EndUpdate(gGameWindow);
- SetPort(oldPort);
- }
-
- //**************************************************************
- //
- // ShowStruckFace - Draw struck face, play strike sound
- // occupant indicates local or opponent strike
- //
- //**************************************************************
- static void ShowStruckFace(short h, short v, short occupant)
- {
- gSquare[h][v].occupant = occupant;
- gSquare[h][v].age = TickCount();
- DrawSquare(h,v);
- PlayStrikeSound();
- }
-
- //**********************************************************
- //
- // SendGameAction - Codify and send a message
- //
- //**********************************************************
- static OSErr SendGameAction(short action, short h, short v)
- {
- unsigned char c;
-
- //Set to format AAHHHVVV AA = action, HHH = h, VVV = v
- c = action;
- c = c << 3;
- c += h;
- c = c << 3;
- c += v;
-
- return putc(&c);
- }
-
- //**********************************************************
- //
- // SendNewGame - Sends New Game action through connection
- //
- //**********************************************************
- OSErr SendNewGame(void)
- {
- return SendGameAction(kActionNewGame, 0, 0);
- }
-
- //*************************************************************
- //
- // HandleOpponentStrike - Respond to receipt of strike message
- //
- //*************************************************************
- static void HandleOpponentStrike(short h, short v,
- Boolean *inProgress)
- {
- if (++gOpponentStruckCount >= kWinningScore)
- *inProgress = false;
- if (gSquare[h][v].occupant == kHealthy) {
- ShowStruckFace(h, v, kStruckByOpponent);
- // only show strike if necessary, but always give credit
- } // because other side validated face was there when struck
- }
-
- //*************************************************************
- //
- // HandleWindowClick - Respond mouse down event on game window
- //
- //*************************************************************
- void HandleWindowClick(Point p, Boolean *inProgress)
- {
- GrafPtr oldPort;
-
- GetPort(&oldPort);
- SetPort(gGameWindow);
- GlobalToLocal(&p);
- p.h /= kOffset; // find struck square, protect against
- p.v /= kOffset; // invalid h or v; only strike healthy
- if (p.h < kNumCols && p.v < kNumRows
- && gSquare[p.h][p.v].occupant == kHealthy) {
- if (!SendGameAction(kActionStruckFace, p.h, p.v)) {
- if (++gLocalStruckCount >= kWinningScore)
- *inProgress = false;// game ends at kWinningScore
- ShowStruckFace(p.h, p.v, kStruckLocally);
- }
- }
- SetPort(oldPort);
- }
-
- //**********************************************************
- //
- // ShowFace - Show a newly-created healthy face at h, v
- //
- //**********************************************************
- static void ShowFace(short h, short v, Boolean local)
- {
- gSquare[h][v].occupant = kHealthy;
- gSquare[h][v].age = TickCount();
- gSquare[h][v].madeLocally = local;
- DrawSquare(h,v);
- }
-
- //**********************************************************
- //
- // MakeFace - Find an empty square and put a face in it
- //
- //**********************************************************
- static void MakeFace(void)
- {
- short h, v;
- long maxH = kNumCols, maxV = kNumRows;
-
- do {
- h = ((unsigned short)Random() * maxH) / 65536;
- v = ((unsigned short)Random() * maxV) / 65536;
- } while (gSquare[h][v].occupant != kEmpty);
-
- if (SendGameAction(kActionMadeFace, h, v))
- return; // Maybe do something different if send fails
-
- ShowFace(h, v, true);
- gMadeCount++; // keep track of #faces made
- }
-
- //**********************************************************
- //
- // HandleGameAction - Respond to a received game action
- //
- //**********************************************************
- void HandleGameAction(short action, short h, short v,
- Boolean *inProgress)
- {
- if (action == kActionMadeFace)
- ShowFace(h, v, false);
- else if (action == kActionStruckFace)
- HandleOpponentStrike(h, v, inProgress);
- else { // action == kActionNewGame
- NewGame();
- *inProgress = true;
- }
- }
-
- //**********************************************************
- //
- // GetGameAction - Get message and if found decode & handle
- //
- //**********************************************************
- static OSErr GetGameAction(Boolean *inProgress)
- {
- unsigned char c;
- OSErr theErr;
- short action, h, v;
-
- theErr = getc(&c);
- if (c && !theErr) {
- action = c >> 6;
- h = (c >> 3) & 0x7;
- v = c & 0x7;
- HandleGameAction(action, h, v, inProgress);
- }
- return theErr;
- }
-
- //**********************************************************
- //
- // Look for game actions and maintain deaths and births
- //
- //**********************************************************
- void IdleGame(Boolean *inProgress)
- {
- GetGameAction(inProgress); // should trap error returned
- KillStruckFaces();
- if (*inProgress)
- if (gMadeCount < kMaxMade)
- MakeFace();
- }
-